package com.atguigu.tingshu.search.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.LongTermsBucket;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.model.album.*;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.repository.AlbumInfoIndexRepository;
import com.atguigu.tingshu.search.repository.SuggestIndexRepository;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


@Slf4j
@Service
@SuppressWarnings({"all"})
public class SearchServiceImpl implements SearchService {

    @Autowired
    private AlbumFeignClient albumFeignClient;
    @Autowired
    private UserFeignClient userFeignClient;
    @Autowired
    private AlbumInfoIndexRepository albumInfoIndexRepository;
    @Autowired
    private ElasticsearchClient elasticsearchClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;


    @Qualifier("threadPoolTaskExecutor")
    @Autowired
    private Executor executor;
//    @Resource(name = "threadPoolTaskExecutor")
//    private Executor threadPoolTaskExecutor;


    /**
     * 构建专辑索引库文档对象，新增专辑到索引库
     *
     * @param albumId
     */
    @Override
    public void upperAlbum(Long albumId) {
        //创建索引库文档对象
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();
        //封装文档对象中的相关属性
        CompletableFuture<AlbumInfo> albumInfoCompletableFuture = CompletableFuture.supplyAsync(()->{
            //远程调用专辑服务获取专辑信息及标签信息
            AlbumInfo albumInfo = albumFeignClient.getAlbumInfoById(albumId).getData();
            Assert.notNull(albumInfo,"专辑:{}不存在",albumId);
            BeanUtil.copyProperties(albumInfo,albumInfoIndex);
            //封装专辑标签列表到索引库文档对象中
            List<AlbumAttributeValue> albumAttributeValueVoList = albumInfo.getAlbumAttributeValueVoList();
            if(!CollectionUtils.isEmpty(albumAttributeValueVoList)){
                List<AttributeValueIndex> attributeValueIndices = albumAttributeValueVoList
                        .stream()
                        .map(albumAttributeValue ->
                                BeanUtil.copyProperties(albumAttributeValue, AttributeValueIndex.class))
                        .collect(Collectors.toList());
                albumInfoIndex.setAttributeValueIndexList(attributeValueIndices);
            }
            return albumInfo;
        }, executor);

        CompletableFuture<Void> baseCategoryViewCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo->{
            //封装文档对象中分类相关信息
            BaseCategoryView baseCategoryView = albumFeignClient.getCategoryViewByCategory3Id(albumInfo.getCategory3Id()).getData();
            Assert.notNull(baseCategoryView,"分类:{}不存在",albumInfo.getCategory3Id());
            //封装分类ID到索引库文档对象中
            albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
            albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());
        }, executor);


        CompletableFuture<Void> voidCompletableFuture = albumInfoCompletableFuture.thenAcceptAsync(albumInfo -> {
            //封装文档对象中的用户信息
            UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(albumInfo.getUserId()).getData();
            Assert.notNull(userInfoVo,"用户:{}信息为空",albumInfo.getUserId());
            albumInfoIndex.setAnnouncerName(userInfoVo.getNickname());
        },executor);

        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
            //封装文档对象中统计相关信息 暂时采用随机生成方式
            //5.1 封装播放量数值
            int playStatNum = RandomUtil.randomInt(1000, 2000);
            albumInfoIndex.setPlayStatNum(playStatNum);
            //5.2 封装订阅量数值
            int subscribeStatNum = RandomUtil.randomInt(800, 1000);
            albumInfoIndex.setSubscribeStatNum(subscribeStatNum);
            //5.3 封装购买量数值
            int buyStatNum = RandomUtil.randomInt(100, 500);
            albumInfoIndex.setBuyStatNum(buyStatNum);
            //5.4 封装评论量数值
            int commentStatNum = RandomUtil.randomInt(500, 1000);
            albumInfoIndex.setCommentStatNum(commentStatNum);
            //5.5 基于以上生成统计数值计算出当前文档热度分值  热度=累加（不同统计数值*权重）
            BigDecimal bigDecimal1 = new BigDecimal("0.1").multiply(BigDecimal.valueOf(playStatNum));
            BigDecimal bigDecimal2 = new BigDecimal("0.2").multiply(BigDecimal.valueOf(subscribeStatNum));
            BigDecimal bigDecimal3 = new BigDecimal("0.3").multiply(BigDecimal.valueOf(buyStatNum));
            BigDecimal bigDecimal4 = new BigDecimal("0.4").multiply(BigDecimal.valueOf(commentStatNum));
            BigDecimal hotScore = bigDecimal1.add(bigDecimal2).add(bigDecimal3).add(bigDecimal4);
            albumInfoIndex.setHotScore(hotScore.doubleValue());
        },executor);

        CompletableFuture.allOf(
                albumInfoCompletableFuture,
                baseCategoryViewCompletableFuture,
                voidCompletableFuture,
                completableFuture
        ).orTimeout(10, TimeUnit.SECONDS)
                .join();

        //保存专辑索引库文档对象
        albumInfoIndexRepository.save(albumInfoIndex);

        //将专辑标题存入提词索引库
        this.saveSuggestIndex(albumInfoIndex);

        RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
        //判断布隆过滤器是否存在,如果存在则将专辑id存入布隆过滤器
        if(bloomFilter.isExists()){
            bloomFilter.add(albumId);
        }
    }


    @Override
    public void lowerAlbum(Long albumId) {
        albumInfoIndexRepository.deleteById(albumId);
    }

    /**
     * 站内搜索
     * @param albumIndexQuery
     * @return
     */
    @Override
    public AlbumSearchResponseVo search(AlbumIndexQuery albumIndexQuery) {
        try {
            //一、构建完整检索请求对象,包含完整DSL语句请求体参数
            SearchRequest searchRequest = this.buildDSL(albumIndexQuery);
            System.err.println("本次检索DSL,复制到Kibana验证：");
            System.err.println(searchRequest.toString());
            //二、执行检索
            SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest,AlbumInfoIndex.class);
            //三、解析ES响应结果
            return this.parseResult(searchResponse,albumIndexQuery);
        } catch (IOException e) {
            log.error("执行检索失败:{}",e.getMessage());
            throw new RuntimeException(e);
        }
    }


    private static final String INDEX_NAME = "albuminfo";
    /**
     * 构建完整检索请求对象，包含所有DSL请求体参数
     * @param albumIndexQuery 查询条件
     * @return
     */
    @Override
    public SearchRequest buildDSL(AlbumIndexQuery albumIndexQuery) {
        //1.创建检索构造器对象 封装 index索引库名称
        SearchRequest.Builder builder = new SearchRequest.Builder();
        builder.index(INDEX_NAME);
        //2.设置查询条件:请求体参数query
        String keyword = albumIndexQuery.getKeyword();
        //2.1 创建bool查询条件,用于封装所有查询条件
        BoolQuery.Builder allConditionBoolQueryBuilder = new BoolQuery.Builder();
        //2.2 条件1: 关键字 采用must 有算分机制按相关性返回
        if(StringUtils.hasText(keyword)){
            allConditionBoolQueryBuilder.must(m->m.match(m1->m1.field("albumTitle").query(keyword)));
        }
        //2.3 条件2: 一二三级分类ID 采用filter 有缓存机制
        //2.3.1 封装一级分类ID查询条件
        Long category1Id = albumIndexQuery.getCategory1Id();
        if(category1Id != null){
            allConditionBoolQueryBuilder.filter(f->f.term(f1->f1.field("category1Id").value(category1Id)));
        }
        //2.3.2 封装二级分类ID查询条件
        Long category2Id = albumIndexQuery.getCategory2Id();
        if(category2Id!=null){
            allConditionBoolQueryBuilder.filter(f->f.term(f1->f1.field("category2Id").value(category2Id)));
        }
        //2.3.3 封装三级分类ID查询条件
        Long category3Id = albumIndexQuery.getCategory3Id();
        if(category3Id!= null){
            allConditionBoolQueryBuilder.filter(f->f.term(f1->f1.field("category3Id").value(category3Id)));
        }

        //2.4 条件3: 多组标签List集合 每组标签形式=标签ID:标签值ID
        List<String> attributeList = albumIndexQuery.getAttributeList();
        if(!CollectionUtils.isEmpty(attributeList)){
            //2.4.1 每遍历一组标签过滤条件,设置一个Nested查询
            for (String s : attributeList) {
                String[] split = s.split(":");
                if(split!=null && split.length == 2){
                    allConditionBoolQueryBuilder.filter(f->f.nested(n->n.path("attributeValueIndexList")
                            .query(q->q.bool(b->b.must(m->m.term(t->t.field("attributeValueIndexList.attributeId").value(split[0])))
                                    .must(m->m.term(t->t.field("attributeValueIndexList.valueId").value(split[1])))))));
                }
            }
        }
        builder.query(allConditionBoolQueryBuilder.build()._toQuery());
        //3. 设置分页:请求体参数from size
        Integer pageNo = albumIndexQuery.getPageNo();
        Integer pageSize = albumIndexQuery.getPageSize();
        int from = (pageNo - 1) * pageSize;
        builder.from(from).size(pageSize);
        //4. 设置请求体参数"highlight"高亮
        if(StringUtils.hasText(keyword)){
            builder.highlight(h->h.fields("albumTitle", h1->h1.preTags("<font style='color:red'>").postTags("</font>")));
        }
        //5. 设置请求体参数"sort"排序 形式:高亮字段(1,2,3):排序规则
        String order = albumIndexQuery.getOrder();
        if(StringUtils.hasText(order)){
            String[] split = order.split(":");
            if(split!=null && split.length==2){
                //确定排序字段 以及排序方向
                String orderField = "";
                switch (split[0]){
                    case "1":
                        orderField = "hotScore";
                        break;
                    case "2":
                        orderField = "playStatNum";
                        break;
                    case "3":
                        orderField = "createTime";
                        break;
                }
                SortOrder sortOrder = split[1].equals("asc") ? SortOrder.Asc : SortOrder.Desc;
                String finalOrderField = orderField;
                builder.sort(s->s.field(f->f.field(finalOrderField).order(sortOrder)));
            }
        }
        //6. 设置请求体参数"_source"指定字段响应
        builder.source(s->s.filter(f->f.excludes("attributeValueIndexList",
                "hotScore",
                "commentStatNum",
                "buyStatNum",
                "subscribeStatNum",
                "announcerName")));
        //7. 基于检索对象构建器对象返回实际检索请求对象
        return builder.build();
    }

    /**
     * 解析ES返回响应结果
     * @param searchResponse
     * @param albumIndexQuery
     * @return
     */
    @Override
    public AlbumSearchResponseVo parseResult(SearchResponse<AlbumInfoIndex> searchResponse, AlbumIndexQuery albumIndexQuery) {
        //1.创建响应vo对象
        AlbumSearchResponseVo vo = new AlbumSearchResponseVo();
        //2.封装vo中分页相关四项属性
        //2.1 从入参直接获取页码跟页大小
        Integer pageNo = albumIndexQuery.getPageNo();
        Integer pageSize = albumIndexQuery.getPageSize();
        //2.2 从ES响应结果获取总记录数
        long total = searchResponse.hits().total().value();
        //2.3 通过总记录数计算总页数
        long totalPages = total % pageSize == 0 ? total / pageSize : total / pageSize + 1;
        vo.setPageNo(pageNo);
        vo.setPageSize(pageSize);
        vo.setTotal(total);
        vo.setTotalPages(totalPages);

        //3.封装vo中检索到专辑列表集合属性
        List<Hit<AlbumInfoIndex>> hits = searchResponse.hits().hits();
        if(!CollectionUtils.isEmpty(hits)){
            //采用Stream流将检索文档类型从AlbumInfoIndex转为AlbumInfoIndexVo
            List<AlbumInfoIndexVo> list = hits.stream().map(hit -> {
                AlbumInfoIndex albumInfoIndex = hit.source();
                //处理高亮字段
                Map<String, List<String>> stringListMap = hit.highlight();
                if (!CollectionUtils.isEmpty(stringListMap)) {
                    String hightLightText = stringListMap.get("albumTitle").get(0);
                    albumInfoIndex.setAlbumTitle(hightLightText);
                }
                return BeanUtil.copyProperties(albumInfoIndex, AlbumInfoIndexVo.class);
            }).collect(Collectors.toList());
            vo.setList(list);
        }
        //4.响应vo对象
        return vo;
    }

    /**
     * 查询1级分类下置顶3级分类热度TOP6专辑
     *
     * @param category1Id
     * @return [{"baseCategory3":{三级分类对象},list:[专辑列表]},,{其他6个置顶分类热门专辑Map}]
     */
    @Override
   // @SneakyThrows
    public List<Map<String, Object>> channel(Long category1Id) {
        try {
            //1.根据一级分类ID获取到置顶三级分类ID
            //1.1远程调用获取置顶三级分类
            List<BaseCategory3> category3List = albumFeignClient.findTopBaseCategory3(category1Id).getData();
            Assert.notNull(category3List,"一级分类{}置顶三级分类不存在",category1Id);

            //1.2获取置顶三级分类id列表
            //List<FieldValue> fieldValueList = category3List.stream().map(c -> FieldValue.of(c.getId())).collect(Collectors.toList());
            //1.3封装结果map中分类对象，将三级分类List转为Map<Id,分类对象>
//        Map<Long, BaseCategory3> category3Map = category3List.stream().collect(Collectors.toMap(
//                BaseCategory3::getId,
//                baseCategory3 -> baseCategory3
//        ));
//        List<Long> fieldValueList = new ArrayList<>(category3Map.keySet());
            Map<Long, BaseCategory3> category3Map = category3List.stream().collect(Collectors.toMap(BaseCategory3::getId, c -> c));
            List<FieldValue> fieldValueList = category3List.stream().map(c -> FieldValue.of(c.getId())).collect(Collectors.toList());

            //2.调用ES进行检索:置顶分类热门专辑
            SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(s->s.index(INDEX_NAME)
                            .size(0)
                            .query(q -> q.terms(t -> t.field("category3Id").terms(t1 -> t1.value(fieldValueList))))
                            .aggregations("category3_agg",a -> a.terms(t -> t.field("category3Id").size(7))
                                    .aggregations("top6", a1 -> a1.topHits(t -> t.size(6)
                                            .sort(s1 -> s1.field(f -> f.field("hotScore").order(SortOrder.Desc)))
                                            .source(s1 -> s1.filter(f -> f.excludes(
                                                    "attributeValueIndexList",
                                                    "hotScore",
                                                    "commentStatNum",
                                                    "buyStatNum",
                                                    "subscribeStatNum",
                                                    "announcerName"
                                            ))))))
                    ,AlbumInfoIndex.class);

            //3.解析聚合结果
            List<LongTermsBucket> longTermsBuckets = searchResponse.aggregations().get("category3_agg").lterms().buckets().array();
            //3.1 遍历三级分类聚合结果,每遍历一次就封装一个置顶分类热门专辑Map对象
            if(!CollectionUtils.isEmpty(longTermsBuckets)){
                List<Map<String, Object>> mapList = longTermsBuckets.stream().map(bucket -> {
                    //3.1.1 创建Map封装指定分类热门专辑
                    Map<String, Object> map = new HashMap<>();
                    //3.1.2 封装三级分类对象
                    long category3Id = bucket.key();
                    map.put("baseCategory3", category3Map.get(category3Id));
                    //3.1.3 封装对应热度最高top6专辑列表 将子聚合类型转为TopHitsAggregate
                    List<AlbumInfoIndex> top6List = bucket.aggregations()
                            .get("top6")  //获取三级分类聚合内子聚合"top6"
                            .topHits()
                            .hits()
                            .hits()
                            //遍历Hit集合,将集合泛型从Hit转为AlbumInfoIndex类型
                            .stream()
                            .map(hit -> {
                                //采用FastJson将专辑JSON转为索引库文档对象
                                AlbumInfoIndex albumInfoIndex = JSON.parseObject(hit.source().toString(), AlbumInfoIndex.class);
                                return albumInfoIndex;
                            }).collect(Collectors.toList());
                    map.put("list", top6List);
                    return map;
                }).collect(Collectors.toList());
                return mapList;
            }

        } catch (IOException e) {
            e.printStackTrace();

        }
        return null;
    }
    @Autowired
    private SuggestIndexRepository suggestIndexRepository;

    @Override
    public void saveSuggestIndex(AlbumInfoIndex albumInfoIndex) {
        //构建索引库文档对象
        SuggestIndex suggestIndex = new SuggestIndex();
        suggestIndex.setId(albumInfoIndex.getId().toString());
        String albumTitle = albumInfoIndex.getAlbumTitle();
        suggestIndex.setTitle(albumTitle);
        suggestIndex.setKeyword(new Completion(new String[]{albumTitle}));
        // 将汉字转为拼音
        String pinyin = PinyinUtil.getPinyin(albumTitle, "");
        suggestIndex.setKeywordPinyin(new Completion(new String[]{pinyin}));
        //将汉字转为拼音首字母
        String firstLetter = PinyinUtil.getFirstLetter(albumTitle, "");
        suggestIndex.setKeywordSequence(new Completion(new String[]{firstLetter}));
        //存入提词文档记录到提词索引库
        suggestIndexRepository.save(suggestIndex);
    }

    private static final String SUGGEST_INDEX = "suggestinfo";
    @Override
    public List<String> completeSuggest(String keyword) {
        try {
            SearchResponse<SuggestIndex> searchResponse = elasticsearchClient.search(se -> se.index(SUGGEST_INDEX).suggest(
                    s -> s.suggesters("letter-suggest", s1 -> s1.prefix(keyword)
                                    .completion(
                                            c -> c.field("keywordSequence")
                                                    .size(10)
                                                    .skipDuplicates(true)
                                    ))
                            .suggesters("pinyin-suggest", s1 -> s1.prefix(keyword)
                                    .completion(c -> c.field("keywordPinyin")
                                            .size(10)
                                            .skipDuplicates(true)
                                            .fuzzy(f -> f.fuzziness("1"))))
                            .suggesters("keyword-suggest", s1 -> s1.prefix(keyword)
                                    .completion(c -> c.field("keyword")
                                            .size(10)
                                            .skipDuplicates(true))))
                    , SuggestIndex.class);

            //2.解析ES补全结果(去重)
            Set<String> hashSet = new HashSet<>();
            //2.1解析建议结果 通过不同建议参数名获取汉字、拼音等提示词结果
            hashSet.addAll(this.parseSuggestResult(searchResponse,"letter-suggest"));
            hashSet.addAll(this.parseSuggestResult(searchResponse,"pinyin-suggest"));
            hashSet.addAll(this.parseSuggestResult(searchResponse,"keyword-suggest"));
            //如果解析建议提示词列表长度小于10，采用全文查询尝试补全到10个
            if(hashSet.size() < 10){
                SearchResponse<AlbumInfoIndex> search = elasticsearchClient.search(s -> s.index(INDEX_NAME)
                        .query(q -> q.match(m -> m.field("albumTitle").query(keyword)))
                        .source(s1 -> s1.filter(f -> f.includes("albumTitle")))
                        .size(10), AlbumInfoIndex.class);
                List<Hit<AlbumInfoIndex>> hitList = search.hits().hits();
                if(!CollectionUtils.isEmpty(hitList)){
                    for (Hit<AlbumInfoIndex> hit : hitList) {
                        String albumTitle = hit.source().getAlbumTitle();
                        hashSet.add(albumTitle);
                        if(hashSet.size() >= 10){
                            break;
                        }
                    }
                }

            }
            if (hashSet.size() >= 10) {
                //如果提词结果列表中大于10截取前10个
                return new ArrayList<>(hashSet).subList(0, 10);
            } else {
                return new ArrayList<>(hashSet);
            }
        } catch (IOException e) {
            log.error("[搜索服务]关键字自动补全异常：{}", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 解析建议词结果
     *
     * @param searchResponse ES检索结果对象
     * @param suggestName    自定义建议词参数名称
     * @return
     */
    @Override
    public List<String> parseSuggestResult(SearchResponse<SuggestIndex> searchResponse, String suggestName) {
        List<String> list = new ArrayList<>();
        List<Suggestion<SuggestIndex>> suggestionList = searchResponse.suggest().get(suggestName);
        if(!CollectionUtils.isEmpty(suggestionList)){
            //遍历得到建议对象
            for (Suggestion<SuggestIndex> suggestIndexSuggestion : suggestionList) {
                for (CompletionSuggestOption<SuggestIndex> option : suggestIndexSuggestion.completion().options()) {
                    SuggestIndex source = option.source();
                    list.add(source.getTitle());
                }
            }
        }
        return list;
    }

    /**
     * 更新不同分类不同排行TOP20数据
     * @return
     */
    @Override
    public void updateLatelyAlbumRanking() {
        try {
            //远程调用获取所有的一级分类id列表
            List<BaseCategory1> category1List = albumFeignClient.findAllCategory1().getData();
            Assert.notNull(category1List,"一级分类列表为空");
            List<Long> category1IdList = category1List.stream().map(BaseCategory1::getId).collect(Collectors.toList());

            //遍历一级分类id,获取top20
            for (Long categoryId : category1IdList) {
                String key = RedisConstant.RANKING_KEY_PREFIX + categoryId;
                BoundHashOperations<String,String,List<AlbumInfoIndex>> hashOps = redisTemplate.boundHashOps(key);
                String[] rankingDimensionArray
                        = new String[]{"hotScore", "playStatNum", "subscribeStatNum", "buyStatNum", "commentStatNum"};
                for (String rank : rankingDimensionArray) {
                    SearchResponse<AlbumInfoIndex> search = elasticsearchClient.search(s -> s.index(INDEX_NAME).size(20)
                            .query(q -> q.term(t -> t.field("category1Id").value(categoryId)))
                            .sort(s1 -> s1.field(f -> f.field(rank).order(SortOrder.Desc)))
                            .source(s1 -> s1.filter(f -> f.excludes("attributeValueIndexList",
                                    "hotScore",
                                    "commentStatNum",
                                    "buyStatNum",
                                    "subscribeStatNum",
                                    "announcerName"))), AlbumInfoIndex.class);
                    //解析结果
                    List<Hit<AlbumInfoIndex>> hitList = search.hits().hits();
                    if(!CollectionUtils.isEmpty(hitList)){
                        List<AlbumInfoIndex> top20List = hitList.stream().map(hit -> {
                            AlbumInfoIndex source = hit.source();
                            return source;
                        }).collect(Collectors.toList());
                        //将排行数据存入redis中
                        hashOps.put(rank,top20List);
                    }

                }
            }
        } catch (IOException e) {
            log.error("更新Redis小时榜失败！",e);
            throw new RuntimeException(e);
        }

    }

    /**
     * 查询Redis中不同分类下不同维度TOP20专辑列表
     *
     * @param category1Id
     * @param dimension
     * @return
     */
    @Override
    public List<AlbumInfoIndexVo> findRankingList(Long category1Id, String dimension) {
        String key = RedisConstant.RANKING_KEY_PREFIX + category1Id;
        BoundHashOperations<String,String,List<AlbumInfoIndex>> hashOps = redisTemplate.boundHashOps(key);
        //当前绑定键是否存在指定字段(field)  --> hashOps.hasKey(field)
        Boolean isFlag = hashOps.hasKey(dimension);
        if(isFlag){
            List<AlbumInfoIndex> albumInfoIndexList = hashOps.get(dimension);
            //将AlbumInfoIndex转化为AlbumInfoIndexVo
            List<AlbumInfoIndexVo> albumInfoIndexVoList = albumInfoIndexList.stream().map(a -> BeanUtil.copyProperties(a, AlbumInfoIndexVo.class))
                    .collect(Collectors.toList());
            return albumInfoIndexVoList;
        }
        return null;
    }


}
